#include <iostream>
using namespace std;

#include <vector>
#include <algorithm>
#include <queue>
#include <pthread.h>
#include <unistd.h>

#define NUM 5

template <typename T>
class BlockQueue
{
private:
    bool isFull()
    {
        return _q.size() == _capacity;
    }

    bool isEmpty()
    {
        return _q.empty();
    }
public:
    BlockQueue(int capacity = NUM)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full, nullptr);
        pthread_cond_init(&_empty, nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
    }

    void Push(const T& data)
    {
        pthread_mutex_lock(&_mutex);
        while (isFull())
        {
            pthread_cond_wait(&_full, &_mutex);
        }
        _q.push(data);
        pthread_mutex_unlock(&_mutex);
        pthread_cond_signal(&_empty);
    }

    void Pop(T& data)
    {
        pthread_mutex_lock(&_mutex);
        while (isEmpty())
        {
            pthread_cond_wait(&_empty, &_mutex);
        }
        data = _q.front();
        _q.pop();
        pthread_mutex_unlock(&_mutex);
        pthread_cond_signal(&_full);
    }

private:
    queue<T> _q;
    int _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _full;
    pthread_cond_t _empty;
};

void* Producer(void* arg)
{
    BlockQueue<int>* bq = (BlockQueue<int>*)arg;
    while (true)
    {
        sleep(1);
        int data = rand() % 100 + 1;
        bq->Push(data);
        cout << "Producer:" << data << endl;
    }
}

void* Consumer(void* arg)
{
    BlockQueue<int>* bq = (BlockQueue<int>*)arg;
    while (true)
    {
        sleep(1);
        int data;
        bq->Pop(data);
        cout << "Consumer:" << data << endl;
    }
}

int main()
{
    srand((unsigned)time(NULL));

    pthread_t producer, consumer;
    BlockQueue<int>* bq = new BlockQueue<int>;

    pthread_create(&producer, nullptr, Producer, bq);
    pthread_create(&consumer, nullptr, Consumer, bq);

    pthread_join(producer, nullptr);
    pthread_join(consumer, nullptr);

    delete bq;

    return 0;
}